package com.example.bookrecommendationjava.util;

import com.example.bookrecommendationjava.mapper.BookMapper;
import com.example.bookrecommendationjava.mapper.LikeMapper;
import com.example.bookrecommendationjava.mapper.UserMapper;
import com.example.bookrecommendationjava.model.Book;
import com.example.bookrecommendationjava.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @BelongsProject: book-recommendation-java
 * @BelongsPackage: com.example.bookrecommendationjava.util
 * @Author: ZhShy
 * @CreateTime: 2022-03-18 16:58
 * @Description:
 */
@Controller
@RequestMapping("recommend")
@CrossOrigin(origins = "*", maxAge = 3600)
public class Recommend {
    @Autowired
    BookMapper bookMapper;
    @Autowired
    UserMapper userMapper;
    @Autowired
    LikeMapper likeMapper;

    @RequestMapping("recommend")
    @ResponseBody
    public List<Book> recommend(@RequestParam Integer userId) {
        List<Integer> likeBookIdList = likeMapper.selectBookIdFromLikeByUserId(userId);
        User user = userMapper.selectFromUserByUserId(userId);
        user.setBookList(likeBookIdList);
        System.out.println(user.toString());

        List<Integer> userIdList = userMapper.selectUserIdFromUserByUserIdNotEqual(userId);
        List<User> userList = new ArrayList<>();
        for (Integer usersId : userIdList) {
            User otherUser = new User();
            otherUser.setUserId(usersId);
            otherUser.setBookList(likeMapper.selectBookIdFromLikeByUserId(usersId));

            userList.add(otherUser);
        }

        List<User> userBinarizationList = binarization(user, userList);
        for (User userBinarization : userBinarizationList) {
            System.out.println(userBinarization.toString());
        }
        String sql=dataProcessing(userBinarizationList, user);
        return bookMapper.selectFromBookByBookIdInRecommend(sql);
    }

    public List<User> binarization(User user, List<User> userList) {
        int length = bookMapper.selectMaxBookIdFromBook() + 1;
        int[] userBinarization = new int[length];
        for (int i = 0; i < user.getBookList().size(); i++) {
            userBinarization[user.getBookList().get(i)] = 1;
        }

        int[] userListBinarization = new int[length];
        for (int i = 0; i < userList.size(); i++) {
            User otherUser = userList.get(i);
            for (int j = 0; j < otherUser.getBookList().size(); j++) {
                userListBinarization[otherUser.getBookList().get(j)] = 1;
            }

            Double compare = compare(userListBinarization, userBinarization);
            otherUser.setCos_th(compare);
            userList.set(i, otherUser);
            userListBinarization = new int[length];
        }
        return userList;
    }

    public Double compare(int[] o1, int[] o2) {
        double numerator = 0.0;
        for (int i = 0; i < o1.length; i++) {
            numerator += o1[i] * o2[i];
        }

        double denominator1 = 0.0;
        for (int j : o1) {
            denominator1 += j * j;
        }
        denominator1 = Math.sqrt(denominator1);

        double denominator2 = 0.0;
        for (int j : o2) {
            denominator2 += j * j;
        }
        denominator2 = Math.sqrt(denominator2);

        return numerator / (denominator1 * denominator2);

    }

    public String dataProcessing(List<User> userList, User user) {
        Map<Integer, Integer> map1 = new HashMap<>();
        for (Integer bookId : user.getBookList()) {
            map1.put(bookId, bookId);
        }

        Map<Integer, Integer> map = new HashMap<>();
        for (User value : userList) {
            if (value.getCos_th() > 0 && !value.getUserId().equals(user.getUserId())) {
                Integer[] j = getDifferenceSet(value.getBookList().toArray(new Integer[0]), user.getBookList().toArray(new Integer[0]));
                for (Integer integer : j) {
                    //如果其余的用户所购买撒谎那个品的id不在当前用的所购买商品的id，那么就存起来
                    if (!map1.containsKey(integer)) {
                        //存储时，数量每次都+1，方便后面排序，出现的次数多，说明被推荐的机会越高
                        map.put(integer, map.containsKey(integer) ? (map.get(integer) + 1) : 1);
                    }
                }
            }
        }
        System.out.println("处理之后的map：");
        for (Integer key : map.keySet()) {
            System.out.println("商品id=" + key + "--用户所购数量=" + map.get(key));
        }

        //把map进行降序排序
        Map<Integer, Integer> map2 = sortByKeyDesc(map);
        System.out.println("按降序" + map2);


        //拼接成一个sql，方便去查数据库
        StringBuilder sqlId = new StringBuilder();
        for (Integer key : map2.keySet()) {
            sqlId.append(key).append(",");
        }

        sqlId = new StringBuilder(sqlId.substring(0, sqlId.length() - 1));

        System.out.println("最终拿到的被推荐给当前用户的商品id--->" + sqlId);

        return sqlId.toString();
    }

    public Integer[] getDifferenceSet(Integer[] m, Integer[] n) {
        // 将较长的数组转换为set
        Set<Integer> set = new HashSet<Integer>(Arrays.asList(m.length > n.length ? m : n));

        // 遍历较短的数组，实现最少循环
        for (Integer i : m.length > n.length ? n : m) {
            // 如果集合里有相同的就删掉，如果没有就将值添加到集合
            if (set.contains(i)) {
                set.remove(i);
            } else {
                set.add(i);
            }
        }

        Integer[] arr = {};
        return set.toArray(arr);
    }

    public static <K, V> Map<K, V> sortByKeyDesc(Map<K, V> originMap) {
        if (originMap == null) {
            return null;
        }
        return sort(originMap, comparatorByKeyDesc);
    }

    private static Comparator<Map.Entry> comparatorByKeyDesc = (Map.Entry o1, Map.Entry o2) -> {
        if (o1.getKey() instanceof Comparable) {
            return ((Comparable) o2.getKey()).compareTo(o1.getKey());
        }
        throw new UnsupportedOperationException("键的类型尚未实现Comparable接口");
    };

    private static <K, V> Map<K, V> sort(Map<K, V> originMap, Comparator<Map.Entry> comparator) {
        return originMap.entrySet()
                .stream()
                .sorted(comparator)
                .collect(
                        Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e2,
                                LinkedHashMap::new));
    }


}
